Skip to content

Conversation

@gilles-peskine-arm
Copy link
Contributor

@gilles-peskine-arm gilles-peskine-arm commented Feb 25, 2025

Defragment incoming TLS handshake messages incrementally, instead of all at the end. This makes the code simpler and a little more efficient.

Fixes defragmentation of encrypted messages with an explicit IV.

PR checklist

@gilles-peskine-arm gilles-peskine-arm added component-tls needs-ci Needs to pass CI tests size-s Estimated task size: small (~2d) priority-high High priority - will be reviewed soon labels Feb 25, 2025
@minosgalanakis minosgalanakis self-requested a review February 26, 2025 17:20
@mpg mpg mentioned this pull request Feb 28, 2025
6 tasks
@gilles-peskine-arm gilles-peskine-arm force-pushed the tls-defragment-incremental-dev branch 2 times, most recently from 06508ac to c75da8b Compare March 3, 2025 16:54
@gilles-peskine-arm gilles-peskine-arm added the needs-preceding-pr Requires another PR to be merged first label Mar 3, 2025
@gilles-peskine-arm gilles-peskine-arm requested a review from mpg March 3, 2025 16:59
@gilles-peskine-arm gilles-peskine-arm force-pushed the tls-defragment-incremental-dev branch 3 times, most recently from 129337e to 41e53cf Compare March 3, 2025 21:33
@gilles-peskine-arm gilles-peskine-arm force-pushed the tls-defragment-incremental-dev branch 3 times, most recently from ae8c65b to ce818da Compare March 4, 2025 09:37
Copy link
Contributor

@minosgalanakis minosgalanakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revied the range of commits specific to the change of fragment's re-assemly.

Looks good overall

#else
size_t const in_buf_len = MBEDTLS_SSL_IN_BUFFER_LEN;
#endif
if (payload_end + ssl->in_hsfraglen > ssl->in_buf + in_buf_len) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume that when MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH is enabled, a buffer can only expand when resized?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. A buffer can shrink when resized. But resizing only happens at the beginning and at the end of the handshake.

In any case, I don't think that matters here? We're checking whether the data fits now. If there's code that wants to resize the buffer, it would have to determine whether the data that's already in the buffer would still fit. Which, currently, is easy: resizing is only done at times when there's no data in the buffer.

@gilles-peskine-arm
Copy link
Contributor Author

Here's a final rebase now that #10021 is merged.

@gilles-peskine-arm gilles-peskine-arm force-pushed the tls-defragment-incremental-dev branch from ce818da to e25976f Compare March 5, 2025 15:59
Pacify `clang -Wdocumentation`.

Signed-off-by: Gilles Peskine <[email protected]>
In preparation for reworking mbedtls_ssl_prepare_handshake_record(), tweak
the "waiting for more handshake fragments" log message in
ssl_consume_current_message(), and add a similar one in
mbedtls_ssl_prepare_handshake_record(). Assert both.

Signed-off-by: Gilles Peskine <[email protected]>
In preparation for reworking mbedtls_ssl_prepare_handshake_record(),
tweak the "handshake fragment:" log message.

This changes what information is displayed when a record contains data
beyond the expected end of the handshake message. This case is currently
untested and its handling will change in a subsequent commit.

Signed-off-by: Gilles Peskine <[email protected]>
Minor refactoring of the initial checks and preparation when receiving the
first fragment. Use `ssl->in_hsfraglen` to determine whether there is a
pending handshake fragment, for consistency, and possibly for more
robustness in case handshake fragments are mixed with non-handshake
records (although this is not currently supported anyway).

Signed-off-by: Gilles Peskine <[email protected]>
Reassemble handshake fragments incrementally instead of all at the end. That
is, every time we receive a non-initial handshake fragment, append it to the
initial fragment. Since we only have to deal with at most two handshake
fragments at the same time, this simplifies the code (no re-parsing of a
record) and is a little more memory-efficient (no need to store one record
header per record).

This commit also fixes a bug. The previous code did not calculate offsets
correctly when records use an explicit IV, which is the case in TLS 1.2 with
CBC (encrypt-then-MAC or not), GCM and CCM encryption (i.e. all but null and
ChachaPoly). This led to the wrong data when an encrypted handshake message
was fragmented (Finished or renegotiation). The new code handles this
correctly.

Signed-off-by: Gilles Peskine <[email protected]>
Pacify `clang -Wformat-pedantic`.

Signed-off-by: Gilles Peskine <[email protected]>
@gilles-peskine-arm gilles-peskine-arm force-pushed the tls-defragment-incremental-dev branch from e25976f to 1051a2e Compare March 5, 2025 16:02
@gilles-peskine-arm gilles-peskine-arm added needs-review Every commit must be reviewed by at least two team members, needs-backports Backports are missing or are pending review and approval. and removed needs-ci Needs to pass CI tests needs-preceding-pr Requires another PR to be merged first labels Mar 5, 2025
minosgalanakis
minosgalanakis previously approved these changes Mar 5, 2025
Copy link
Contributor

@minosgalanakis minosgalanakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

The pointer to the framework may need to be updated before merging, since we are failing component_test_tls13_only_psk because all tests are being skipped.

Changed log messages and added more tests in
`tests/opt-testcases/handshake-generated.sh`.

Signed-off-by: Gilles Peskine <[email protected]>
@gilles-peskine-arm
Copy link
Contributor Author

I had missed some commits when rebasing the framework. I've updated the framework branch and the pointer again, should be good now.

Copy link
Contributor

@mpg mpg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good overall, but:

  • I'm afraid one of the bounds check is wrong (it's probably not needed anyway, but if we have it it should be correct).
  • Unless I'm mistaken there's an uncommented limitation compared to what the standard allows, which should have a comment.

ssl->in_hslen));

const size_t hs_remain = ssl->in_hslen - ssl->in_hsfraglen;
if (ssl->in_msglen > hs_remain) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC we're being stricter than the protocol here: this condition means that there's more data in the record after the rest of this handshake message, presumably the (beginning of) the next handshake message, which is allowed by the standard unless I'm mistaken.

I don't have an issue with rejecting it anyway, but I'd like a comment explaining that we are rejecting something the standard allows and why we're doing that. Also, that's a limitation we should document. And I find the current debug message a bit confusing.

(Unless I'm mistaken about what's happening here.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right.

I added this check because I believed that the old code silently ignored subsequent handshake messages if there is more than one in the same record. However, revisiting this, I'm not sure that was the case: here we do nothing, but ssl_consume_current_message handles it. So now I'm worried that I've broken some existing untested behavior. But if I remove the check here, I'm worried that the defragmentation code may be creating an unexpected state. I'll keep digging today.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leveraging #9976, I've crafted a test case with ServerHello and ServerCertificate in the same message, and that test case passes before incremental defragmentation. So this check removes functionality, and I'm going to remove it.

#else
size_t const in_buf_len = MBEDTLS_SSL_IN_BUFFER_LEN;
#endif
if (payload_end + ssl->in_hsfraglen > ssl->in_buf + in_buf_len) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you mean payload_end + ssl->in_msglen here, to protect the call to memmove() below.

Signed-off-by: Gilles Peskine <[email protected]>
A handshake record may contain multiple handshake messages, or multiple
fragments (there can be the final fragment of a pending message, then zero
or more whole messages, and an initial fragment of an incomplete message).
This was previously untested, but supported, so don't break it.

Signed-off-by: Gilles Peskine <[email protected]>
There is no longer any different processing at this point, just
near-identical log messages.

Signed-off-by: Gilles Peskine <[email protected]>
Signed-off-by: Gilles Peskine <[email protected]>
Copy link
Contributor

@mpg mpg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking pretty good! Just one issue in a debug string, and a suggestion to avoid potentially adding useless memory accesses even in the non-fragmented case.

Signed-off-by: Gilles Peskine <[email protected]>
Copy link
Contributor

@minosgalanakis minosgalanakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@mpg mpg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@github-project-automation github-project-automation bot moved this from In Development to Has Approval in Roadmap pull requests (new board) Mar 7, 2025
@mpg mpg added approved Design and code approved - may be waiting for CI or backports and removed needs-review Every commit must be reviewed by at least two team members, labels Mar 7, 2025
@gilles-peskine-arm gilles-peskine-arm merged commit 723fec4 into Mbed-TLS:features/tls-defragmentation/development Mar 7, 2025
4 of 6 checks passed
@github-project-automation github-project-automation bot moved this from Has Approval to Done in Roadmap pull requests (new board) Mar 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Design and code approved - may be waiting for CI or backports component-tls needs-backports Backports are missing or are pending review and approval. priority-high High priority - will be reviewed soon size-s Estimated task size: small (~2d)

Development

Successfully merging this pull request may close these issues.

3 participants